//! Konfiguracijska datoteka za 'arh' sloj

#ifndef _ARH_SPREMNIK_H_
#define _ARH_SPREMNIK_H_

#ifndef	_ARH_PREKIDI_S_

#include "tipovi.h"

//svaki proces ima svoj opisnik spremnika (dretve istog procesa ga dijele)
typedef struct _arh_spremnik_t_ {
	//dio za "koristenje segmenata"
	void	*pocetna_adresa;

	uint	odmak;	//pomak za pretvaranje iz apsolutne u relativnu adresu
			//i obrnuto

	int	razina;	//3 za korisnicke procese, 0 za jezgrine

	uint16	op_koda;
	uint16	op_podataka;

	uint32	velicina; //velicina adresnog prostora procesa (u oktetima)

	//dio za "stranicenje"
	uint32	cr0;	//kontrolni registar 0 (nacin rada, ...)
	uint32	cr3;	//kontrolni registar 3 (adresa direktorija stranica)
			//opisnik stranicenja!

	bool	stranicenje; //u sustavu stranicenja?

} arh_spremnik_t;

//postavljanje pocetnih tavblica i opisnika spremnika
void arh_inicijalizacija_spremnika ();

//zauzimanje spremnika pri stvaranju novog procesa
void arh_zauzmi_spremnik ( arh_spremnik_t *spremnik, void *_pocetak,
			   uint _velicina, bool u_jezgri, bool stranicenje );

//pocetni stog za novu dretvu
void arh_pripremi_novu_dretvu ( void *spremnik,
			        void *a_kontekst, void *l_kontekst,
				void *a_stog, void *l_stog,
			        void *pokreni_dretvu,
			        void *poc_funkc, void *param );
//prije vracanja u dretvu
void arh_pripravi_okolinu_dretvi ( void *_kontekst );

//po prekidu, prije same obrade
inline void *arh_pohrani_okolinu_dretve ( void *_kontekst );

//pretvara adresu jezgre u adresu za program
inline void *arh_jup(void *_spremnik, void *adr_u_jezgri);

//pretvara adresu programa u adresu koja je dohvatljiva iz jezgre
inline void *arh_puj(void *_spremnik, void *adr_u_programu);

#endif /* !_ARH_PREKIDI_S_ */

#define SEG_NULL	0	//nulti zapis u GDT-u = 0
#define SEGJ_INSTR	1	//kod za jezgrine funkcije
#define SEGJ_PODACI	2	//podaci za jezgru
#define SEG_TSS		3	//TSS
#define SEG_INSTR	4	//kod za programe (dretve)
#define SEG_PODACI	5	//podaci za programe (dretve)
//azirirati ako se mijenja GDT!

#define GDT	0
#define LDT	1

#define RAZINA_JEZGRE		0
#define RAZINA_KORISNIKA	3

#define GDT_OPISNIK(ID, TABLICA, PRIV_RAZINA)			\
	( (PRIV_RAZINA) | ((TABLICA) << 2) | ((ID) << 3) )

#ifdef _ARH_SPREMNIK_C_

static void arh_azuriraj_opisnik_segmenta (int id, void *poc_adr, uint32 vel,
					   int razina);
//static void arh_ispisi_gdt_zapis(int id);

//zapisi u GDT
typedef struct _gdt_t_ {
	uint16	segm_limit0;	//segment limit, bitovi: 15:00		(00-15)
	uint16	base_addr0;	//pocetna adresa, bitovi: 15:00		(16-31)
	uint8	base_addr1;	//pocetna adresa, bitovi: 23:16		(32-38)
	uint8	type	: 4;	//tip segmenta				(39-42)
	uint8	S	: 1;	//tip opisnika: 0-system, 1-kod ili pod.(43-43)
	uint8	DPL	: 2;	//razina prioriteta opisnika		(44-45)
	uint8	P	: 1;	//prisutan (u mem.)			(46-46)
	uint8	segm_limit1: 4;	//segment limit, bitovi: 19:16		(47-50)
	uint8	AVL	: 1;	//"Available for use by system software"(51-51)
	uint8	L	: 1;	//64-bitni segment koda? (inace nebitan)(52-52)
	uint8	DB	: 1;	//1 za 32 bitne sustave, 0 za 16 bitne	(53-53)
	uint8	G	: 1;	//granularnost 0-1B, 1-4kB (za rac.lim.)(54-54)
	uint8	base_addr2;	//pocetna adresa, bitovi: 31:24		(55-63)
	
} __attribute__((__packed__)) gdt_t;

//GDTR (registar)
typedef struct _gdtr_t_ {
	uint16 limit;
	gdt_t *gdt;
} __attribute__((__packed__)) gdtr_t;


//opisnik za zadatak - TSS
typedef struct _tss_t_ {
	uint32	op_preth;	//ne koristi se

	uint32	esp0;		//kazaljka stoga za obradu prekida (prsten 0)!
	uint16	ss0;		//segment za stog - prsten 0
	uint16	ss0popuna;

	uint32	esp1, ss1, esp2, ss2; //prsteni 1 i 2 se ne koriste

	uint32	cr3;		//kontrolni registar cr3 (stranicenje)

	//mjesto za "kontekst" - ne koristi se!
	uint32	eip, eflags, reg[8], seg[6];

	uint32	ldt;		//opisnik za LDT tablicu (ne koristi se)

	uint16	zamka;		//ne koristi se;

	uint16	ui_mapa;	//koji su UI dozvoljeni
} __attribute__((__packed__)) tss_t;


//prvi zapis u gdt-u je prazan (mora biti!)
#define GDT_0	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

/* svih 4 GB za kod (r-x) */
#define GDT_INSTRUKCIJE_JEZGRE			\
	{	0xffff,	/* segm_limit0	*/	\
		0,	/* base_addr0	*/	\
		0,	/* base_addr1	*/	\
		0x0a,	/* type	r-x	*/	\
		1,	/* S		*/	\
		0,	/* DPL		*/	\
		1,	/* P		*/	\
		0x0f,	/* segm_limit1	*/	\
		0,	/* AVL		*/	\
		0,	/* L		*/	\
		1,	/* DB		*/	\
		1,	/* G		*/	\
		0	/* base_addr2	*/	\
	}

/* svih 4 GB za podatke (rw-)*/
#define GDT_PODACI_JEZGRE			\
	{	0xffff,	/* segm_limit0	*/	\
		0,	/* base_addr0	*/	\
		0,	/* base_addr1	*/	\
		0x02,	/* type	rw-	*/	\
		1,	/* S		*/	\
		0,	/* DPL		*/	\
		1,	/* P		*/	\
		0x0f,	/* segm_limit1	*/	\
		0,	/* AVL		*/	\
		0,	/* L		*/	\
		1,	/* DB		*/	\
		1,	/* G		*/	\
		0	/* base_addr2	*/	\
	}

/* svih 4 GB za kod (r-x) */
#define GDT_INSTRUKCIJE_PROGRAMA		\
	{	0xffff,	/* segm_limit0	*/	\
		0,	/* base_addr0	*/	\
		0,	/* base_addr1	*/	\
		0x0a,	/* type	r-x	*/	\
		1,	/* S		*/	\
		3,	/* DPL		*/	\
		1,	/* P		*/	\
		0x0f,	/* segm_limit1	*/	\
		0,	/* AVL		*/	\
		0,	/* L		*/	\
		1,	/* DB		*/	\
		1,	/* G		*/	\
		0	/* base_addr2	*/	\
	}

/* svih 4 GB za podatke (rw-)*/
#define GDT_PODACI_PROGRAMA			\
	{	0xffff,	/* segm_limit0	*/	\
		0,	/* base_addr0	*/	\
		0,	/* base_addr1	*/	\
		0x02,	/* type	rw-	*/	\
		1,	/* S		*/	\
		3,	/* DPL		*/	\
		1,	/* P		*/	\
		0x0f,	/* segm_limit1	*/	\
		0,	/* AVL		*/	\
		0,	/* L		*/	\
		1,	/* DB		*/	\
		1,	/* G		*/	\
		0	/* base_addr2	*/	\
	}


/* TSS - "opisnik zadataka" podrzan od strane procesora */
#define GDT_TSS					\
	{	sizeof(tss_t),	/* segm_limit0	*/	\
		0,	/* base_addr0	*/	\
		0,	/* base_addr1	*/	\
		0x09,	/* 10B1 = 1001	*/	\
		0,	/* -		*/	\
		0,	/* DPL		*/	\
		1,	/* P		*/	\
		0x00,	/* segm_limit1	*/	\
		0,	/* AVL		*/	\
		0,	/* L		*/	\
		0,	/* DB		*/	\
		0,	/* G		*/	\
		0	/* base_addr2	*/	\
	}

#endif	/* _ARH_SPREMNIK_C_ */

//za stranicenje
#if	defined(_ARH_SPREMNIK_C_) || defined(_J_SPREMNIK_C_) ||	\
	defined(_J_STRANICENJE_C_) || defined(_J_PROCESI_C_) ||	\
	defined(_ARH_KONTEKST_C_)

#define	VEL_IND_1	10 //bar ovaj mora biti > 0
#define	VEL_IND_2	10
#define	VEL_IND_3	0

#define	POMAK		12
#define	VEL_IND		( VEL_IND_1 + VEL_IND_2 + VEL_IND_3 )

#define	IMA_IND_2	( VEL_IND_2 > 0 )
#define	IMA_IND_3	( VEL_IND_3 > 0 )

#define	VEL_STRANICE	( 1 << POMAK )

#define	CR0_STRANICENJE	0x80000001	//omogucavanje stranicenja ('8')

#define	IZ_ADR(ADR)	( ( (aint) ADR ) >> POMAK )
#define	U_ADR(IND)	( (void *) ( (IND) << POMAK ) )


//intelova arhitektura
#define	STR_PRISUTNA	(1<<0)
#define	STR_PISI	(1<<1)
#define	STR_KORISNICKA	(1<<2)
#define	STR_KORISTENA	(1<<5)
#define	STR_MIJENJANA	(1<<6)

#define	OZNACI_PRISUTNA(op)		(op)->zast |= STR_PRISUTNA
#define	OZNACI_NIJE_PRISUTNA(op)	(op)->zast &= ~STR_PRISUTNA

#define	OZNACI_CITAJ_PISI(op)		(op)->zast |= STR_PISI
#define	OZNACI_CITAJ(op)		(op)->zast &= ~STR_PISI

#define	OZNACI_ZA_JEZGRU(op)		(op)->zast &= ~STR_KORISNICKA
#define	OZNACI_ZA_DRETVU(op)		(op)->zast |= STR_KORISNICKA

#define	PROVJERI_PRISUTNOST(op)		( (op)->zast & STR_PRISUTNA )


//posebne oznake stranica/okvira - zadnja 3 bita zastavica (11-9)
#define	STR_ALOCIRANA		1<<9	//u primarnom ili sekundarnom spremniku
#define	STR_REZERVIRANA		2<<9	//samo rezervirana (ne postoji)
#define	STR_NIJE_REZERVIRANA	0<<9	//nije ni rezervirana!

#define	OZNACI_ALOCIRANA(op)	\
	(op)->zast = ( (op)->zast & 0xfffff1ff ) | STR_ALOCIRANA
#define	OZNACI_REZERVIRANA(op)	\
	(op)->zast = ( (op)->zast & 0xfffff1ff ) | STR_REZERVIRANA
#define	OZNACI_NIJE_REZERVIRANA(op)	\
	(op)->zast = ( (op)->zast & 0xfffff1ff ) | STR_NIJE_REZERVIRANA

#define	PROVJERI_ALOCIRANA(op)		( (op)->zast & STR_ALOCIRANA )
#define	PROVJERI_REZERVIRANA(op)	( (op)->zast & STR_REZERVIRANA )
#define	PROVJERI_NIJE_NI_REZERVIRANA(op)	\
	( !( PROVJERI_ALOCIRANA ( op ) || PROVJERI_REZERVIRANA ( op ) ) )


static inline void *arh_dohvati_adresu_promasaja ()
{
	uint32 adresa;

	__asm__ ( " mov %%cr2, %0 " : "=r" (adresa) );

	return (void *) adresa;
}

#endif //_ARH_SPREMNIK_C_ || _J_SPREMNIK_C_ || _J_STRANICENJE_C_

#endif	/* _ARH_SPREMNIK_H_ */
